<?php

/**
 * Erra_Core_HttpFacade - HTTP object to handle main document
 *
 * @package
 * @author Most of the code was ripped from urlHandler class by Olivier Meunier & Association Dotclear
 * @copyright Copyright (c) 2012 - Erra Prod
 * @version $1.0$
 * @access public
 */
class Erra_Core_HttpFacade {

    static public $httpSingleton = null;
    protected $types = array();
    protected $default_handler;
    protected $error_handlers = array();
    public $mode;
    public $type = 'default';

    public function __construct($mode='path_info') {
        $this->mode = $mode;
    }

    /**
     * Erra_Core_HttpFacade::getSingleton() - Gets instance singleton
     *
     * @return
     */
    public static function getSingleton() {
        if (is_null(self::$httpSingleton)) {
            self::$httpSingleton = new Erra_Core_HttpFacade();
        }
        return self::$httpSingleton;
    }

    public function register($type, $url, $representation, $handler) {
        $this->types[$type] = array(
            'url' => $url,
            'representation' => $representation,
            'handler' => $handler
        );
    }

    public function registerDefault($handler) {
        $this->default_handler = $handler;
    }

    public function registerError($handler) {
        array_unshift($this->error_handlers, $handler);
    }

    public function unregister($type) {
        if (isset($this->types[$type])) {
            unset($this->types[$type]);
        }
    }

    public function getTypes() {
        return $this->types;
    }

    public function getBase($type) {
        if (isset($this->types[$type])) {
            return $this->types[$type]['url'];
        }
        return null;
    }

    public function getDocument() {
        $type = $args = '';

        if ($this->mode == 'path_info') {
            $part = substr($_SERVER['PATH_INFO'], 1);
        } else {
            $part = '';

            $qs = $this->parseQueryString();

            # Recreates some _GET and _REQUEST pairs
            if (!empty($qs)) {
                foreach ($_GET as $k => $v) {
                    if (isset($_REQUEST[$k])) {
                        unset($_REQUEST[$k]);
                    }
                }
                $_GET = $qs;
                $_REQUEST = array_merge($qs, $_REQUEST);

                list($k, $v) = each($qs);
                if ($v === null) {
                    $part = $k;
                    unset($_GET[$k]);
                    unset($_REQUEST[$k]);
                }
            }
        }

        $_SERVER['URL_REQUEST_PART'] = $part;

        $this->getArgs($part, $type, $args);

        if (!$type) {
            $this->type = 'default';
            $this->callDefaultHandler($args);
        } else {
            $this->type = $type;
            $this->callHandler($type, $args);
        }
    }

    public function getArgs($part, &$type, &$args) {
        if ($part == '') {
            $type = null;
            $args = null;
            return;
        }

        $this->sortTypes();

        foreach ($this->types as $k => $v) {
            $repr = $v['representation'];
            if ($repr == $part) {
                $type = $k;
                $args = null;
                return;
            } elseif (preg_match('#' . $repr . '#', $part, $m)) {
                $type = $k;
                $args = isset($m[1]) ? $m[1] : null;
                return;
            }
        }

        # No type, pass args to default
        $args = $part;
    }

    public function callHandler($type, $args) {
        if (!isset($this->types[$type])) {
            throw new Exception('Unknown URL type');
        }

        $handler = $this->types[$type]['handler'];
        if (!is_callable($handler)) {
            throw new Exception('Unable to call function');
        }
        try {
            call_user_func($handler, $args);
        } catch (Exception $e) {
            foreach ($this->error_handlers as $err_handler) {
                if (call_user_func($err_handler, $args, $type, $e) === true) {
                    return;
                }
            }
            # propagate exception, as it has not been processed by handlers
            throw $e;
        }
    }

    public function callDefaultHandler($args) {
        if (!is_callable($this->default_handler)) {
            throw new Exception('Unable to call function');
        }

        try {
            call_user_func($this->default_handler, $args);
        } catch (Exception $e) {
            foreach ($this->error_handlers as $err_handler) {
                if (call_user_func($err_handler, $args, 'default', $e) === true) {
                    return;
                }
            }
            # propagate exception, as it has not been processed by handlers
            throw $e;
        }
    }

    protected function parseQueryString() {
        if (!empty($_SERVER['QUERY_STRING'])) {
            $q = explode('&', $_SERVER['QUERY_STRING']);
            $T = array();
            foreach ($q as $v) {
                $t = explode('=', $v, 2);

                $t[0] = rawurldecode($t[0]);
                if (!isset($t[1])) {
                    $T[$t[0]] = null;
                } else {
                    $T[$t[0]] = urldecode($t[1]);
                }
            }

            return $T;
        }
        return array();
    }

    protected function sortTypes() {
        foreach ($this->types as $k => $v) {
            $r[$k] = $v['url'];
        }
        array_multisort($r, SORT_DESC, $this->types);
    }

}

?>